{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from IPython.display import Image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<img src=\"HASYv2/hasy-data/v2-00010.png\"/>"
      ],
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# example image from the dataset\n",
    "\n",
    "Image(url=\"HASYv2/hasy-data/v2-00010.png\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Using TensorFlow backend.\n"
     ]
    }
   ],
   "source": [
    "import csv\n",
    "from PIL import Image as pil_image\n",
    "import keras.preprocessing.image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# load all images (as numpy arrays) and save their classes\n",
    "\n",
    "imgs = []\n",
    "classes = []\n",
    "with open('HASYv2/hasy-data-labels.csv') as csvfile:\n",
    "    csvreader = csv.reader(csvfile)\n",
    "    i = 0\n",
    "    for row in csvreader:\n",
    "        if i > 0:\n",
    "            img = keras.preprocessing.image.img_to_array(pil_image.open(\"HASYv2/\" + row[0]))\n",
    "            # neuron activation functions behave best when input values are between 0.0 and 1.0 (or -1.0 and 1.0),\n",
    "            # so we rescale each pixel value to be in the range 0.0 to 1.0 instead of 0-255\n",
    "            img /= 255.0\n",
    "            imgs.append((row[0], row[2], img))\n",
    "            classes.append(row[2])\n",
    "        i += 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('hasy-data/v2-00000.png', 'A', array([[[ 1.,  1.,  1.],\n",
       "         [ 1.,  1.,  1.],\n",
       "         [ 1.,  1.,  1.],\n",
       "         ..., \n",
       "         [ 1.,  1.,  1.],\n",
       "         [ 1.,  1.,  1.],\n",
       "         [ 1.,  1.,  1.]],\n",
       " \n",
       "        [[ 1.,  1.,  1.],\n",
       "         [ 1.,  1.,  1.],\n",
       "         [ 1.,  1.,  1.],\n",
       "         ..., \n",
       "         [ 1.,  1.,  1.],\n",
       "         [ 1.,  1.,  1.],\n",
       "         [ 1.,  1.,  1.]],\n",
       " \n",
       "        [[ 1.,  1.,  1.],\n",
       "         [ 1.,  1.,  1.],\n",
       "         [ 1.,  1.,  1.],\n",
       "         ..., \n",
       "         [ 1.,  1.,  1.],\n",
       "         [ 1.,  1.,  1.],\n",
       "         [ 1.,  1.,  1.]],\n",
       " \n",
       "        ..., \n",
       "        [[ 1.,  1.,  1.],\n",
       "         [ 1.,  1.,  1.],\n",
       "         [ 1.,  1.,  1.],\n",
       "         ..., \n",
       "         [ 1.,  1.,  1.],\n",
       "         [ 1.,  1.,  1.],\n",
       "         [ 1.,  1.,  1.]],\n",
       " \n",
       "        [[ 1.,  1.,  1.],\n",
       "         [ 1.,  1.,  1.],\n",
       "         [ 1.,  1.,  1.],\n",
       "         ..., \n",
       "         [ 1.,  1.,  1.],\n",
       "         [ 1.,  1.,  1.],\n",
       "         [ 1.,  1.,  1.]],\n",
       " \n",
       "        [[ 1.,  1.,  1.],\n",
       "         [ 1.,  1.,  1.],\n",
       "         [ 1.,  1.,  1.],\n",
       "         ..., \n",
       "         [ 1.,  1.,  1.],\n",
       "         [ 1.,  1.,  1.],\n",
       "         [ 1.,  1.,  1.]]], dtype=float32))"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "imgs[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "168233"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(imgs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# shuffle the data, split into 80% train, 20% test\n",
    "\n",
    "import random\n",
    "random.shuffle(imgs)\n",
    "split_idx = int(0.8*len(imgs))\n",
    "train = imgs[:split_idx]\n",
    "test = imgs[split_idx:]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "train_input = np.asarray(list(map(lambda row: row[2], train)))\n",
    "test_input = np.asarray(list(map(lambda row: row[2], test)))\n",
    "\n",
    "train_output = np.asarray(list(map(lambda row: row[1], train)))\n",
    "test_output = np.asarray(list(map(lambda row: row[1], test)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from sklearn.preprocessing import LabelEncoder\n",
    "from sklearn.preprocessing import OneHotEncoder"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Number of classes: 369\n"
     ]
    }
   ],
   "source": [
    "# convert class names into one-hot encoding\n",
    "\n",
    "# first, convert class names into integers\n",
    "label_encoder = LabelEncoder()\n",
    "integer_encoded = label_encoder.fit_transform(classes)\n",
    "\n",
    "# then convert integers into one-hot encoding\n",
    "onehot_encoder = OneHotEncoder(sparse=False)\n",
    "integer_encoded = integer_encoded.reshape(len(integer_encoded), 1)\n",
    "onehot_encoder.fit(integer_encoded)\n",
    "\n",
    "# convert train and test output to one-hot\n",
    "train_output_int = label_encoder.transform(train_output)\n",
    "train_output = onehot_encoder.transform(train_output_int.reshape(len(train_output_int), 1))\n",
    "test_output_int = label_encoder.transform(test_output)\n",
    "test_output = onehot_encoder.transform(test_output_int.reshape(len(test_output_int), 1))\n",
    "\n",
    "num_classes = len(label_encoder.classes_)\n",
    "print(\"Number of classes: %d\" % num_classes)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from keras.models import Sequential\n",
    "from keras.layers import Dense, Dropout, Flatten\n",
    "from keras.layers import Conv2D, MaxPooling2D"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "conv2d_1 (Conv2D)            (None, 30, 30, 32)        896       \n",
      "_________________________________________________________________\n",
      "max_pooling2d_1 (MaxPooling2 (None, 15, 15, 32)        0         \n",
      "_________________________________________________________________\n",
      "conv2d_2 (Conv2D)            (None, 13, 13, 32)        9248      \n",
      "_________________________________________________________________\n",
      "max_pooling2d_2 (MaxPooling2 (None, 6, 6, 32)          0         \n",
      "_________________________________________________________________\n",
      "flatten_1 (Flatten)          (None, 1152)              0         \n",
      "_________________________________________________________________\n",
      "dense_1 (Dense)              (None, 1024)              1180672   \n",
      "_________________________________________________________________\n",
      "dropout_1 (Dropout)          (None, 1024)              0         \n",
      "_________________________________________________________________\n",
      "dense_2 (Dense)              (None, 369)               378225    \n",
      "=================================================================\n",
      "Total params: 1,569,041\n",
      "Trainable params: 1,569,041\n",
      "Non-trainable params: 0\n",
      "_________________________________________________________________\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "model = Sequential()\n",
    "model.add(Conv2D(32, kernel_size=(3, 3), activation='relu',\n",
    "                 input_shape=np.shape(train_input[0])))\n",
    "model.add(MaxPooling2D(pool_size=(2, 2)))\n",
    "model.add(Conv2D(32, (3, 3), activation='relu'))\n",
    "model.add(MaxPooling2D(pool_size=(2, 2)))\n",
    "model.add(Flatten())\n",
    "model.add(Dense(1024, activation='tanh'))\n",
    "model.add(Dropout(0.5))\n",
    "model.add(Dense(num_classes, activation='softmax'))\n",
    "\n",
    "model.compile(loss='categorical_crossentropy', optimizer='adam',\n",
    "              metrics=['accuracy'])\n",
    "\n",
    "print(model.summary())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import keras.callbacks\n",
    "tensorboard = keras.callbacks.TensorBoard(log_dir='./logs/mnist-style')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train on 107668 samples, validate on 26918 samples\n",
      "Epoch 1/10\n",
      " - 54s - loss: 1.5568 - acc: 0.6243 - val_loss: 0.9898 - val_acc: 0.7257\n",
      "Epoch 2/10\n",
      " - 52s - loss: 0.9820 - acc: 0.7281 - val_loss: 0.8964 - val_acc: 0.7501\n",
      "Epoch 3/10\n",
      " - 52s - loss: 0.8730 - acc: 0.7523 - val_loss: 0.8776 - val_acc: 0.7531\n",
      "Epoch 4/10\n",
      " - 52s - loss: 0.8067 - acc: 0.7662 - val_loss: 0.8391 - val_acc: 0.7629\n",
      "Epoch 5/10\n",
      " - 52s - loss: 0.7520 - acc: 0.7771 - val_loss: 0.8406 - val_acc: 0.7579\n",
      "Epoch 6/10\n",
      " - 52s - loss: 0.7137 - acc: 0.7868 - val_loss: 0.8607 - val_acc: 0.7586\n",
      "Epoch 7/10\n",
      " - 52s - loss: 0.6812 - acc: 0.7922 - val_loss: 0.8696 - val_acc: 0.7648\n",
      "Epoch 8/10\n",
      " - 52s - loss: 0.6544 - acc: 0.7984 - val_loss: 0.8581 - val_acc: 0.7655\n",
      "Epoch 9/10\n",
      " - 52s - loss: 0.6312 - acc: 0.8015 - val_loss: 0.8518 - val_acc: 0.7595\n",
      "Epoch 10/10\n",
      " - 52s - loss: 0.6125 - acc: 0.8076 - val_loss: 0.8854 - val_acc: 0.7609\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<keras.callbacks.History at 0x7ff4c41b6ef0>"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model.fit(train_input, train_output,\n",
    "          batch_size=32,\n",
    "          epochs=10,\n",
    "          verbose=2,\n",
    "          validation_split=0.2,\n",
    "          callbacks=[tensorboard])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Test loss: 0.886258037221\n",
      "Test accuracy: 0.762207626241\n"
     ]
    }
   ],
   "source": [
    "score = model.evaluate(test_input, test_output, verbose=2)\n",
    "print('Test loss:', score[0])\n",
    "print('Test accuracy:', score[1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Conv2D count: 1, Dense size: 128, Dropout: 0.00 - Loss: 1.16, Accuracy: 0.74, Time: 419 sec\n",
      "Conv2D count: 1, Dense size: 128, Dropout: 0.25 - Loss: 0.92, Accuracy: 0.76, Time: 447 sec\n",
      "Conv2D count: 1, Dense size: 128, Dropout: 0.50 - Loss: 0.82, Accuracy: 0.77, Time: 452 sec\n",
      "Conv2D count: 1, Dense size: 128, Dropout: 0.75 - Loss: 0.79, Accuracy: 0.77, Time: 458 sec\n",
      "Conv2D count: 1, Dense size: 256, Dropout: 0.00 - Loss: 1.30, Accuracy: 0.74, Time: 430 sec\n",
      "Conv2D count: 1, Dense size: 256, Dropout: 0.25 - Loss: 1.12, Accuracy: 0.76, Time: 459 sec\n",
      "Conv2D count: 1, Dense size: 256, Dropout: 0.50 - Loss: 0.96, Accuracy: 0.77, Time: 461 sec\n",
      "Conv2D count: 1, Dense size: 256, Dropout: 0.75 - Loss: 0.78, Accuracy: 0.78, Time: 461 sec\n",
      "Conv2D count: 1, Dense size: 512, Dropout: 0.00 - Loss: 1.60, Accuracy: 0.74, Time: 440 sec\n",
      "Conv2D count: 1, Dense size: 512, Dropout: 0.25 - Loss: 1.43, Accuracy: 0.75, Time: 466 sec\n",
      "Conv2D count: 1, Dense size: 512, Dropout: 0.50 - Loss: 1.24, Accuracy: 0.75, Time: 471 sec\n",
      "Conv2D count: 1, Dense size: 512, Dropout: 0.75 - Loss: 0.87, Accuracy: 0.77, Time: 475 sec\n",
      "Conv2D count: 1, Dense size: 1024, Dropout: 0.00 - Loss: 2.13, Accuracy: 0.72, Time: 480 sec\n",
      "Conv2D count: 1, Dense size: 1024, Dropout: 0.25 - Loss: 1.94, Accuracy: 0.73, Time: 517 sec\n",
      "Conv2D count: 1, Dense size: 1024, Dropout: 0.50 - Loss: 1.59, Accuracy: 0.73, Time: 526 sec\n",
      "Conv2D count: 1, Dense size: 1024, Dropout: 0.75 - Loss: 0.98, Accuracy: 0.76, Time: 527 sec\n",
      "Conv2D count: 1, Dense size: 2048, Dropout: 0.00 - Loss: 2.00, Accuracy: 0.70, Time: 587 sec\n",
      "Conv2D count: 1, Dense size: 2048, Dropout: 0.25 - Loss: 2.02, Accuracy: 0.70, Time: 629 sec\n",
      "Conv2D count: 1, Dense size: 2048, Dropout: 0.50 - Loss: 1.55, Accuracy: 0.72, Time: 631 sec\n",
      "Conv2D count: 1, Dense size: 2048, Dropout: 0.75 - Loss: 1.29, Accuracy: 0.73, Time: 636 sec\n",
      "Conv2D count: 2, Dense size: 128, Dropout: 0.00 - Loss: 0.87, Accuracy: 0.76, Time: 531 sec\n",
      "Conv2D count: 2, Dense size: 128, Dropout: 0.25 - Loss: 0.79, Accuracy: 0.77, Time: 570 sec\n",
      "Conv2D count: 2, Dense size: 128, Dropout: 0.50 - Loss: 0.74, Accuracy: 0.78, Time: 568 sec\n",
      "Conv2D count: 2, Dense size: 128, Dropout: 0.75 - Loss: 0.79, Accuracy: 0.77, Time: 573 sec\n",
      "Conv2D count: 2, Dense size: 256, Dropout: 0.00 - Loss: 0.99, Accuracy: 0.76, Time: 550 sec\n",
      "Conv2D count: 2, Dense size: 256, Dropout: 0.25 - Loss: 0.85, Accuracy: 0.77, Time: 583 sec\n",
      "Conv2D count: 2, Dense size: 256, Dropout: 0.50 - Loss: 0.77, Accuracy: 0.78, Time: 579 sec\n",
      "Conv2D count: 2, Dense size: 256, Dropout: 0.75 - Loss: 0.76, Accuracy: 0.78, Time: 587 sec\n",
      "Conv2D count: 2, Dense size: 512, Dropout: 0.00 - Loss: 1.18, Accuracy: 0.75, Time: 554 sec\n",
      "Conv2D count: 2, Dense size: 512, Dropout: 0.25 - Loss: 1.07, Accuracy: 0.75, Time: 583 sec\n",
      "Conv2D count: 2, Dense size: 512, Dropout: 0.50 - Loss: 0.84, Accuracy: 0.77, Time: 576 sec\n",
      "Conv2D count: 2, Dense size: 512, Dropout: 0.75 - Loss: 0.77, Accuracy: 0.78, Time: 587 sec\n",
      "Conv2D count: 2, Dense size: 1024, Dropout: 0.00 - Loss: 1.14, Accuracy: 0.74, Time: 557 sec\n",
      "Conv2D count: 2, Dense size: 1024, Dropout: 0.25 - Loss: 0.94, Accuracy: 0.76, Time: 594 sec\n",
      "Conv2D count: 2, Dense size: 1024, Dropout: 0.50 - Loss: 0.86, Accuracy: 0.76, Time: 599 sec\n",
      "Conv2D count: 2, Dense size: 1024, Dropout: 0.75 - Loss: 0.81, Accuracy: 0.77, Time: 602 sec\n",
      "Conv2D count: 2, Dense size: 2048, Dropout: 0.00 - Loss: 1.03, Accuracy: 0.75, Time: 572 sec\n",
      "Conv2D count: 2, Dense size: 2048, Dropout: 0.25 - Loss: 1.09, Accuracy: 0.74, Time: 612 sec\n",
      "Conv2D count: 2, Dense size: 2048, Dropout: 0.50 - Loss: 0.97, Accuracy: 0.74, Time: 616 sec\n",
      "Conv2D count: 2, Dense size: 2048, Dropout: 0.75 - Loss: 0.85, Accuracy: 0.76, Time: 619 sec\n"
     ]
    }
   ],
   "source": [
    "# try various model configurations and parameters to find the best\n",
    "\n",
    "import time\n",
    "\n",
    "results = []\n",
    "for conv2d_count in [1, 2]:\n",
    "    for dense_size in [128, 256, 512, 1024, 2048]:\n",
    "        for dropout in [0.0, 0.25, 0.50, 0.75]:\n",
    "            model = Sequential()\n",
    "            for i in range(conv2d_count):\n",
    "                if i == 0:\n",
    "                    model.add(Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=np.shape(train_input[0])))\n",
    "                else:\n",
    "                    model.add(Conv2D(32, kernel_size=(3, 3), activation='relu'))\n",
    "                model.add(MaxPooling2D(pool_size=(2, 2)))\n",
    "            model.add(Flatten())\n",
    "            model.add(Dense(dense_size, activation='tanh'))\n",
    "            if dropout > 0.0:\n",
    "                model.add(Dropout(dropout))\n",
    "            model.add(Dense(num_classes, activation='softmax'))\n",
    "\n",
    "            model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])\n",
    "\n",
    "            log_dir = './logs/conv2d_%d-dense_%d-dropout_%.2f' % (conv2d_count, dense_size, dropout)\n",
    "            tensorboard = keras.callbacks.TensorBoard(log_dir=log_dir)\n",
    "\n",
    "            start = time.time()\n",
    "            model.fit(train_input, train_output, batch_size=32, epochs=10,\n",
    "                      verbose=0, validation_split=0.2, callbacks=[tensorboard])\n",
    "            score = model.evaluate(test_input, test_output, verbose=2)\n",
    "            end = time.time()\n",
    "            elapsed = end - start\n",
    "            print(\"Conv2D count: %d, Dense size: %d, Dropout: %.2f - Loss: %.2f, Accuracy: %.2f, Time: %d sec\" % \\\n",
    "                 (conv2d_count, dense_size, dropout, score[0], score[1], elapsed))\n",
    "            results.append((conv2d_count, dense_size, dropout, score[0], score[1], elapsed))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "conv2d_68 (Conv2D)           (None, 30, 30, 32)        896       \n",
      "_________________________________________________________________\n",
      "max_pooling2d_68 (MaxPooling (None, 15, 15, 32)        0         \n",
      "_________________________________________________________________\n",
      "conv2d_69 (Conv2D)           (None, 13, 13, 32)        9248      \n",
      "_________________________________________________________________\n",
      "max_pooling2d_69 (MaxPooling (None, 6, 6, 32)          0         \n",
      "_________________________________________________________________\n",
      "flatten_45 (Flatten)         (None, 1152)              0         \n",
      "_________________________________________________________________\n",
      "dense_89 (Dense)             (None, 128)               147584    \n",
      "_________________________________________________________________\n",
      "dropout_34 (Dropout)         (None, 128)               0         \n",
      "_________________________________________________________________\n",
      "dense_90 (Dense)             (None, 369)               47601     \n",
      "=================================================================\n",
      "Total params: 205,329\n",
      "Trainable params: 205,329\n",
      "Non-trainable params: 0\n",
      "_________________________________________________________________\n",
      "None\n",
      "Epoch 1/10\n",
      " - 80s - loss: 1.8212 - acc: 0.5797\n",
      "Epoch 2/10\n",
      " - 77s - loss: 1.0929 - acc: 0.7030\n",
      "Epoch 3/10\n",
      " - 77s - loss: 0.9790 - acc: 0.7278\n",
      "Epoch 4/10\n",
      " - 77s - loss: 0.9189 - acc: 0.7408\n",
      "Epoch 5/10\n",
      " - 77s - loss: 0.8839 - acc: 0.7479\n",
      "Epoch 6/10\n",
      " - 77s - loss: 0.8560 - acc: 0.7528\n",
      "Epoch 7/10\n",
      " - 77s - loss: 0.8360 - acc: 0.7582\n",
      "Epoch 8/10\n",
      " - 77s - loss: 0.8161 - acc: 0.7610\n",
      "Epoch 9/10\n",
      " - 77s - loss: 0.8020 - acc: 0.7653\n",
      "Epoch 10/10\n",
      " - 77s - loss: 0.7913 - acc: 0.7657\n"
     ]
    }
   ],
   "source": [
    "# rebuild/retrain a model with the best parameters (from the search) and use all data\n",
    "model = Sequential()\n",
    "model.add(Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=np.shape(train_input[0])))\n",
    "model.add(MaxPooling2D(pool_size=(2, 2)))\n",
    "model.add(Conv2D(32, (3, 3), activation='relu'))\n",
    "model.add(MaxPooling2D(pool_size=(2, 2)))\n",
    "model.add(Flatten())\n",
    "model.add(Dense(128, activation='tanh'))\n",
    "model.add(Dropout(0.5))\n",
    "model.add(Dense(num_classes, activation='softmax'))\n",
    "model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])\n",
    "print(model.summary())\n",
    "# join train and test data so we train the network on all data we have available to us\n",
    "model.fit(np.concatenate((train_input, test_input)),\n",
    "          np.concatenate((train_output, test_output)),\n",
    "          batch_size=32, epochs=10, verbose=2)\n",
    "\n",
    "# save the trained model\n",
    "model.save(\"mathsymbols.model\")\n",
    "\n",
    "# save label encoder (to reverse one-hot encoding)\n",
    "np.save('classes.npy', label_encoder.classes_)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "conv2d_68 (Conv2D)           (None, 30, 30, 32)        896       \n",
      "_________________________________________________________________\n",
      "max_pooling2d_68 (MaxPooling (None, 15, 15, 32)        0         \n",
      "_________________________________________________________________\n",
      "conv2d_69 (Conv2D)           (None, 13, 13, 32)        9248      \n",
      "_________________________________________________________________\n",
      "max_pooling2d_69 (MaxPooling (None, 6, 6, 32)          0         \n",
      "_________________________________________________________________\n",
      "flatten_45 (Flatten)         (None, 1152)              0         \n",
      "_________________________________________________________________\n",
      "dense_89 (Dense)             (None, 128)               147584    \n",
      "_________________________________________________________________\n",
      "dropout_34 (Dropout)         (None, 128)               0         \n",
      "_________________________________________________________________\n",
      "dense_90 (Dense)             (None, 369)               47601     \n",
      "=================================================================\n",
      "Total params: 205,329\n",
      "Trainable params: 205,329\n",
      "Non-trainable params: 0\n",
      "_________________________________________________________________\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "# load the pre-trained model and predict the math symbol for an arbitrary image;\n",
    "# the code below could be placed in a separate file\n",
    "\n",
    "import keras.models\n",
    "model2 = keras.models.load_model(\"mathsymbols.model\")\n",
    "print(model2.summary())\n",
    "\n",
    "# restore the class name to integer encoder\n",
    "label_encoder2 = LabelEncoder()\n",
    "label_encoder2.classes_ = np.load('classes.npy')\n",
    "\n",
    "def predict(img_path):\n",
    "    newimg = keras.preprocessing.image.img_to_array(pil_image.open(img_path))\n",
    "    newimg /= 255.0\n",
    "\n",
    "    # do the prediction\n",
    "    prediction = model2.predict(newimg.reshape(1, 32, 32, 3))\n",
    "\n",
    "    # figure out which output neuron had the highest score, and reverse the one-hot encoding\n",
    "    inverted = label_encoder2.inverse_transform([np.argmax(prediction)]) # argmax finds highest-scoring output\n",
    "    print(\"Prediction: %s, confidence: %.2f\" % (inverted[0], np.max(prediction)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Prediction: A, confidence: 0.87\n"
     ]
    }
   ],
   "source": [
    "# grab an image (we'll just use a random training image for demonstration purposes)\n",
    "predict(\"HASYv2/hasy-data/v2-00010.png\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Prediction: \\pi, confidence: 0.58\n"
     ]
    }
   ],
   "source": [
    "predict(\"HASYv2/hasy-data/v2-00500.png\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Prediction: \\alpha, confidence: 0.88\n"
     ]
    }
   ],
   "source": [
    "predict(\"HASYv2/hasy-data/v2-00700.png\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
